import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.util.ArrayList;


public class Snake {
	private int x;
	private int y;
	private int snakeLength = 5;
	private int gridSize = 50;
	private int minGridSize = 20;
	private int maxGridSize = 50;
	private int gridLevel = 1;
	private int gridUpgradeLevel = 1;
	private int maxGridUpgradeLevel = 3;
	private int gridUpgradeCost = 30;
	private Environment e;
	private char direction = 's';
	private char lastDirection = 's';
	private boolean looping = false;
	private int growthRate = 3;
	private int foodNumber = 1;
	private int foodUpgradeLevel = 1;
	private int maxFoodNumber = 3;
	private int foodUpgradeCost = 10;
	private int slowmoFrames = 20;
	private boolean isSlowmo = false;
	
	private int points = 330;

	private ArrayList<snakeChunk> snakeBody = new ArrayList<snakeChunk>();
	private ArrayList<Food> foodList = new ArrayList<Food>();
	
	public Snake(Environment e){
		this.e=e;
		x = gridSize/2;
		y = gridSize/2;
	}
	
	public Graphics2D paint(Graphics2D gimage){
		for(int i=0;i<foodList.size();i++){
			gimage=foodList.get(i).paint(gimage);
		}
		for(int i=0;i<snakeBody.size();i++){
			gimage=snakeBody.get(i).paint(gimage);
		}
		gimage.setFont(e.getD().getSmallText());
		gimage.setColor(new Color(76,76,255));
		int[] xcoords = {400,400+5*slowmoFrames,400+5*slowmoFrames};
		int[] ycoords = {590,590,590-slowmoFrames};
		gimage.fill(new Polygon(xcoords,ycoords,xcoords.length));
		gimage.drawLine(400,590,500,570);
		gimage.drawLine(400,590,500,590);
		gimage.drawLine(500,570,500,590);
		return gimage;
	}
	
	public Graphics2D Update(Graphics2D gimage){
		lastDirection=direction;
		move(direction);
		gimage=paint(gimage);
		placeFood();
		checkForEating();
		checkForDying();
		
		checkForWrongFood();

		if(slowmoFrames>0 && isSlowmo){
			slowmoFrames--;
			e.setSleepTime(e.getSleepTime()*4);
		}
		
		return gimage;
	}
	
	public void slowmo(){
		isSlowmo=true;
	}
	
	public void upgradeFood(){
		if(foodNumber<foodUpgradeLevel){
			foodNumber++;
		}
		else if(points>=foodUpgradeCost && foodUpgradeLevel<maxFoodNumber){
			points-=foodUpgradeCost;
			foodNumber++;
			foodUpgradeLevel++;
			foodUpgradeCost*=10;
		}
	}
	
	public void downgradeFood(){
		if(foodNumber>1){
			foodNumber--;
		}
	}
	
	public void upgradeGrid(){
		if(gridLevel<gridUpgradeLevel){
			changeGridLevel(gridLevel+1);
		}else if(points>=gridUpgradeCost){
			points-=gridUpgradeCost;
			gridUpgradeCost*=10;
			changeGridLevel(gridLevel+1);
		}
	}
	
	public void changeGridLevel(int newLevel){
		gridLevel=newLevel;
		if(gridLevel == 1){
			gridSize=50;
		}else if(gridLevel == 2){
			gridSize=25;
		}else{
			gridSize=20;
		}
		snakeBody.clear();
		x=gridSize/2;
		y=gridSize/2;
	}
	
	public void downgradeGrid(){
		if(gridLevel>1){
			changeGridLevel(gridLevel-1);
		}
	}
	
	private void checkForDying(){
		for(int a=1;a<snakeBody.size();a++){
			if(snakeBody.get(a).getX()==x && snakeBody.get(a).getY()==y){
				die();
			}
		}
	}
	
	private void die(){
		snakeBody.clear();
		foodList.clear();
		snakeLength=5;
		x=gridSize/2;
		y=gridSize/2;
		e.setUpgrading(true);
	}

	public char getDirection() {
		return direction;
	}

	public void setDirection(char direction) {
		this.direction = direction;
	}

	private void checkForEating(){
		for(int a=0;a<foodList.size();a++){
			if(foodList.get(a).getX()==x && foodList.get(a).getY()==y){
				snakeLength+=growthRate;
				foodList.remove(a);
				setPoints(getPoints() + 1);
				if(slowmoFrames<20){
					slowmoFrames+=2;
					if(slowmoFrames>20){
						slowmoFrames=20;
					}
				}
			}
		}
	}
	
	private void checkForWrongFood(){
		for(int a=0;a<foodList.size();a++){
			if(foodList.get(a).getX()>=gridSize || foodList.get(a).getY()>=gridSize){
				foodList.remove(a);
			}
		}
	}
	
	private void placeFood(){
		if(foodList.size()<foodNumber){
			int i=0,j=0;
			boolean restart = true;
			int loops=0;
			while(restart && loops<5){
				restart = false;
				i=e.randInt(gridSize-1);
				j=e.randInt(gridSize-1);
				for(int a=0;a<foodList.size();a++){
					if(foodList.get(a).getX()==i && foodList.get(a).getY()==j){
						restart = true;
					}
				}
				for(int a=0;a<snakeBody.size();a++){
					if(snakeBody.get(a).getX()==i && snakeBody.get(a).getY()==j){
						restart = true;
					}
				}
				loops++;
				checkForDying();
			}
			foodList.add(new Food(i,j,e,gridSize));
		}
	}
	
	public void keyPressed(char letter){
		if(letter=='w' && lastDirection!='s'){
			setDirection('n');
		}
		else if(letter=='a' && lastDirection!='e'){
			setDirection('w');
		}
		else if(letter=='s' && lastDirection!='n'){
			setDirection('s');
		}
		else if(letter=='d' && lastDirection!='w'){
			setDirection('e');
		}else if(letter == ' '){
			isSlowmo=true;
		}
	}
	
	public void keyReleased(char letter){
		if(letter == ' '){
			isSlowmo=false;
		}
	}
	
	private void move(char direction){
		if(direction=='s'){
			y++;
		}else if(direction=='n'){
			y--;
		}
		else if(direction=='e'){
			x++;
		}else{
			x--;
		}
		if(looping){
			if(x>=gridSize){
				x=0;
			}else if(x<0){
				x=gridSize-1;
			}
			if(y>=gridSize){
				y=0;
			}else if(y<0){
				y=gridSize-1;
			}
		}
		else{
			if(x>=gridSize || x<0 || y>=gridSize || y<0){
				die();
			}
		}
		if(snakeBody.size()==snakeLength){
			snakeBody.remove(snakeLength-1);
		}
		snakeBody.add(0,new snakeChunk(x,y,e,gridSize));
	}
	
	
	public int getFoodNumber() {
		return foodNumber;
	}

	public void setFoodNumber(int foodNumber) {
		if(foodNumber>0 && foodNumber<=foodUpgradeLevel){
			this.foodNumber = foodNumber;
		}
	}

	/**
	 * @return the gridSize
	 */
	public int getGridSize() {
		return gridSize;
	}

	/**
	 * @param gridSize the gridSize to set
	 */
	public void setGridSize(int gridSize) {
		this.gridSize = gridSize;
	}
	
	/**
	 * @return the x
	 */
	public int getX() {
		return x;
	}

	/**
	 * @param x the x to set
	 */
	public void setX(int x) {
		this.x = x;
	}

	/**
	 * @return the y
	 */
	public int getY() {
		return y;
	}

	/**
	 * @param y the y to set
	 */
	public void setY(int y) {
		this.y = y;
	}

	/**
	 * @return the points
	 */
	public int getPoints() {
		return points;
	}

	/**
	 * @param points the points to set
	 */
	public void setPoints(int points) {
		this.points = points;
	}

	/**
	 * @return the snakeLength
	 */
	public int getSnakeLength() {
		return snakeLength;
	}

	/**
	 * @param snakeLength the snakeLength to set
	 */
	public void setSnakeLength(int snakeLength) {
		this.snakeLength = snakeLength;
	}

	/**
	 * @return the lastDirection
	 */
	public char getLastDirection() {
		return lastDirection;
	}

	/**
	 * @param lastDirection the lastDirection to set
	 */
	public void setLastDirection(char lastDirection) {
		this.lastDirection = lastDirection;
	}

	/**
	 * @return the looping
	 */
	public boolean isLooping() {
		return looping;
	}

	/**
	 * @param looping the looping to set
	 */
	public void setLooping(boolean looping) {
		this.looping = looping;
	}

	/**
	 * @return the growthRate
	 */
	public int getGrowthRate() {
		return growthRate;
	}

	/**
	 * @param growthRate the growthRate to set
	 */
	public void setGrowthRate(int growthRate) {
		this.growthRate = growthRate;
	}

	/**
	 * @return the foodUpgradeLevel
	 */
	public int getFoodUpgradeLevel() {
		return foodUpgradeLevel;
	}

	/**
	 * @param foodUpgradeLevel the foodUpgradeLevel to set
	 */
	public void setFoodUpgradeLevel(int foodUpgradeLevel) {
		this.foodUpgradeLevel = foodUpgradeLevel;
	}

	/**
	 * @return the maxFoodNumber
	 */
	public int getMaxFoodNumber() {
		return maxFoodNumber;
	}

	/**
	 * @param maxFoodNumber the maxFoodNumber to set
	 */
	public void setMaxFoodNumber(int maxFoodNumber) {
		this.maxFoodNumber = maxFoodNumber;
	}

	/**
	 * @return the foodUpgradeCost
	 */
	public int getFoodUpgradeCost() {
		return foodUpgradeCost;
	}

	/**
	 * @param foodUpgradeCost the foodUpgradeCost to set
	 */
	public void setFoodUpgradeCost(int foodUpgradeCost) {
		this.foodUpgradeCost = foodUpgradeCost;
	}

	/**
	 * @return the snakeBody
	 */
	public ArrayList<snakeChunk> getSnakeBody() {
		return snakeBody;
	}

	/**
	 * @param snakeBody the snakeBody to set
	 */
	public void setSnakeBody(ArrayList<snakeChunk> snakeBody) {
		this.snakeBody = snakeBody;
	}

	/**
	 * @return the foodList
	 */
	public ArrayList<Food> getFoodList() {
		return foodList;
	}

	/**
	 * @param foodList the foodList to set
	 */
	public void setFoodList(ArrayList<Food> foodList) {
		this.foodList = foodList;
	}

	/**
	 * @return the minGridSize
	 */
	public int getMinGridSize() {
		return minGridSize;
	}

	/**
	 * @param minGridSize the minGridSize to set
	 */
	public void setMinGridSize(int minGridSize) {
		this.minGridSize = minGridSize;
	}

	/**
	 * @return the maxGridSize
	 */
	public int getMaxGridSize() {
		return maxGridSize;
	}

	/**
	 * @param maxGridSize the maxGridSize to set
	 */
	public void setMaxGridSize(int maxGridSize) {
		this.maxGridSize = maxGridSize;
	}

	/**
	 * @return the gridUpgradeCost
	 */
	public int getGridUpgradeCost() {
		return gridUpgradeCost;
	}

	/**
	 * @param gridUpgradeCost the gridUpgradeCost to set
	 */
	public void setGridUpgradeCost(int gridUpgradeCost) {
		this.gridUpgradeCost = gridUpgradeCost;
	}

	/**
	 * @return the maxGridUpgradeLevel
	 */
	public int getMaxGridUpgradeLevel() {
		return maxGridUpgradeLevel;
	}

	/**
	 * @param maxGridUpgradeLevel the maxGridUpgradeLevel to set
	 */
	public void setMaxGridUpgradeLevel(int maxGridUpgradeLevel) {
		this.maxGridUpgradeLevel = maxGridUpgradeLevel;
	}

	/**
	 * @return the gridUpgradeLevel
	 */
	public int getGridUpgradeLevel() {
		return gridUpgradeLevel;
	}

	/**
	 * @param gridUpgradeLevel the gridUpgradeLevel to set
	 */
	public void setGridUpgradeLevel(int gridUpgradeLevel) {
		this.gridUpgradeLevel = gridUpgradeLevel;
	}
}
